/* $Id */

%{
#include <stdio.h>
#include <math.h>        /* for pow() function       */
#include "symtab.h"

%}

%union {
    double dval;       /* values of expressions     */
    int ival;
    symrec *sym;
    symrec *udsym;
    symrec *idsym;
    symrec *uisym;
    symrec *iisym;
    symrec *ubsym;
    symrec *ibsym;
    }

%token <dval> DOUBLE
%token <ival> INT
%token <ival> TRUE
%token <ival> FALSE
%token <udsym> UD
%token <idsym> ID
%token <uisym> UI
%token <iisym> II
%token <ubsym> UB
%token <ibsym> IB
%type  <dval> dval
%type  <ival> ival
%type  <udsym> udsym
%type  <idsym> idsym
%type  <uisym> uisym
%type  <iisym> iisym
%type  <ubsym> ubsym
%type  <ibsym> ibsym

%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%right '^'

%%

input
    :  /* empty */
    |  input line
    ;

line
    : '\n'
    | udsym '=' idsym '\n'    { $1->sym_type = SYM_ID;
                                *($1->value.d) = *($3->value.d); }
    | udsym '=' iisym '\n'    { $1->sym_type = SYM_ID;
                                *($1->value.d) = *($3->value.i) }
    | udsym '=' dval '\n'     { $1->sym_type = SYM_ID;
                                *($1->value.d) = $3 }
    | udsym '=' ival '\n'     { $1->sym_type = SYM_ID;
                                *($1->value.d) = $3 }
    | uisym '=' iisym '\n'    { $1->sym_type = SYM_II;
                                *($1->value.i) = *($3->value.i) }
    | uisym '=' ival '\n'     { $1->sym_type = SYM_II;
                                *($1->value.i) = $3 }
    | ubsym '=' ibsym '\n'    { $1->sym_type = SYM_IB;
                                *($1->value.b) = *($3->value.b) }
    | ubsym '=' TRUE '\n'     { $1->sym_type = SYM_IB;
                                *($1->value.b) = 1 }
    | ubsym '=' FALSE '\n'    { $1->sym_type = SYM_IB;
                                *($1->value.b) = 0 }
    | idsym '\n'              { printf("\t = %.6f\n", *($1->value.d)); }
    | iisym '\n'              { printf("\t = %d\n", *($1->value.i)); }
    | ibsym '\n'              { printf("\t = %s\n", (*($1->value.b)) ? "True" : "False"); }
    | error '\n'              { yyerrok; }
    ;

udsym
    : UD
    ;

idsym
    : ID
    ;

uisym
    : UI
    ;

iisym
    : II
    ;

ubsym
    : UB
    ;

ibsym
    : IB
    ;

dval
    : idsym '+' idsym        { $$ = *($1->value.d) + *($3->value.d); }
    | idsym '+' iisym        { $$ = *($1->value.d) + *($3->value.i); }
    | iisym '+' idsym        { $$ = *($1->value.i) + *($3->value.d); }
    | idsym '+' dval         { $$ = *($1->value.d) + $3; }
    | dval '+' idsym         { $$ = $1 + *($3->value.d); }
    | idsym '+' ival         { $$ = *($1->value.d) + $3; }
    | ival '+' idsym         { $$ = $1 + *($3->value.d); }
    | dval '+' dval          { $$ = $1 + $3; }
    | dval '+' ival          { $$ = $1 + $3; }
    | ival '+' dval          { $$ = $1 + $3; }
    | idsym '-' idsym        { $$ = *($1->value.d) - *($3->value.d); }
    | idsym '-' iisym        { $$ = *($1->value.d) - *($3->value.i); }
    | iisym '-' idsym        { $$ = *($1->value.i) - *($3->value.d); }
    | idsym '-' dval         { $$ = *($1->value.d) - $3; }
    | dval '-' idsym         { $$ = $1 - *($3->value.d); }
    | idsym '-' ival         { $$ = *($1->value.d) - $3; }
    | ival '-' idsym         { $$ = $1 - *($3->value.d); }
    | dval '-' dval          { $$ = $1 - $3; }
    | dval '-' ival          { $$ = $1 - $3; }
    | ival '-' dval          { $$ = $1 - $3; }
    | idsym '*' idsym        { $$ = *($1->value.d) * *($3->value.d); }
    | idsym '*' iisym        { $$ = *($1->value.d) * *($3->value.i); }
    | iisym '*' idsym        { $$ = *($1->value.i) * *($3->value.d); }
    | idsym '*' dval         { $$ = *($1->value.d) * $3; }
    | dval '*' idsym         { $$ = $1 * *($3->value.d); }
    | idsym '*' ival         { $$ = *($1->value.d) * $3; }
    | ival '*' idsym         { $$ = $1 * *($3->value.d); }
    | dval '*' dval          { $$ = $1 * $3; }
    | dval '*' ival          { $$ = $1 * $3; }
    | ival '*' dval          { $$ = $1 * $3; }
    | idsym '/' idsym        { if (*($3->value.d) == 0.0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.d) / *($3->value.d); }
    | idsym '/' iisym        { if (*($3->value.i) == 0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.d) / *($3->value.i); }
    | iisym '/' idsym        { if (*($3->value.d) == 0.0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.i) / *($3->value.d); }
    | idsym '/' dval         { if ($3 == 0.0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.d) / $3; }
    | dval '/' idsym         { if (*($3->value.d) == 0.0)
                                   yyerror("divide by zero");
                               else
                                    $$ = $1 / *($3->value.d); }
    | idsym '/' ival         { if ($3 == 0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.d) / $3; }
    | ival '/' idsym         { if (*($3->value.d) == 0.0)
                                   yyerror("divide by zero");
                               else
                                    $$ = $1 / *($3->value.d); }
    | dval '/' dval          { if ($3 == 0.0)
                                   yyerror("divide by zero");
                               else
                                   $$ = $1 / $3; }
    | dval '/' ival          { if ($3 == 0)
                                   yyerror("divide by zero");
                               else
                                   $$ = $1 / $3; }
    | ival '/' dval          { if ($3 == 0.0)
                                   yyerror("divide by zero");
                               else
                                   $$ = $1 / $3; }
    | '-' idsym %prec UMINUS { $$ = - *($2->value.d); }
    | '-' dval %prec UMINUS  { $$ = - $2; }
    | idsym '^' idsym        { $$ = pow(*($1->value.d), *($3->value.d)); }
    | idsym '^' iisym        { $$ = pow(*($1->value.d), *($3->value.i)); }
    | iisym '^' idsym        { $$ = pow(*($1->value.i), *($3->value.d)); }
    | idsym '^' dval         { $$ = pow(*($1->value.d), $3); }
    | dval '^' idsym         { $$ = pow($1, *($3->value.d)); }
    | idsym '^' ival         { $$ = pow(*($1->value.d), $3); }
    | ival '^' idsym         { $$ = pow($1, *($3->value.d)); }
    | dval '^' dval          { $$ = pow($1,$3); }
    | dval '^' ival          { $$ = pow($1, $3); }
    | ival '^' dval          { $$ = pow($1, $3); }
    | '(' dval ')'           { $$ = $2; }
    |  DOUBLE                { $$ = $1; }
    ;

ival
    : iisym '+' iisym        { $$ = *($1->value.i) + *($3->value.i); }
    | iisym '+' ival         { $$ = *($1->value.i) + $3; }
    | ival '+' iisym         { $$ = $1 + *($3->value.i); }
    | ival '+' ival          { $$ = $1 + $3; }
    | iisym '-' iisym        { $$ = *($1->value.i) - *($3->value.i); }
    | iisym '-' ival         { $$ = *($1->value.i) - $3; }
    | ival '-' iisym         { $$ = $1 - *($3->value.i); }
    | ival '-' ival          { $$ = $1 - $3; }
    | iisym '*' iisym        { $$ = *($1->value.i) * *($3->value.i); }
    | iisym '*' ival         { $$ = *($1->value.i) * $3; }
    | ival '*' iisym         { $$ = $1 * *($3->value.i); }
    | ival '*' ival          { $$ = $1 * $3; }
    | iisym '/' iisym        { if (*($3->value.i) == 0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.i) / *($3->value.i); }
    | iisym '/' ival         { if ($3 == 0)
                                   yyerror("divide by zero");
                               else
                                    $$ = *($1->value.i) / $3; }
    | ival '/' iisym         { if (*($3->value.i) == 0)
                                   yyerror("divide by zero");
                               else
                                    $$ = $1 / *($3->value.i); }
    | ival '/' ival          { if ($3 == 0)
                                   yyerror("divide by zero");
                               else
                                   $$ = $1 / $3; }
    | '-' iisym %prec UMINUS { $$ = - *($2->value.i); }
    | '-' ival %prec UMINUS  { $$ = - $2; }
    | iisym '^' iisym        { $$ = pow(*($1->value.i), *($3->value.i)); }
    | iisym '^' ival         { $$ = pow(*($1->value.i), $3); }
    | ival '^' iisym         { $$ = pow($1, *($3->value.i)); }
    | ival '^' ival          { $$ = pow($1,$3); }
    | '(' ival ')'           { $$ = $2; }
    | INT                    { $$ = $1; }
    ;

%%

yyerror (char *s)  /* Called by yyparse on error */
{
  fprintf (stderr, "\terror: %s\n", s);
}

int yywrap()
{
    return 1;
}

main ()
{
    double dtmp, da, db;
    int itmp;
    int ba, bb;

    putsym_d("dtmp", &dtmp);
    putsym_d("da", &da);
    putsym_d("db", &db);
    putsym_i("itmp", &itmp);
    putsym_b("ba", &ba);
    putsym_b("bb", &bb);

    yyparse ();
}
